home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Personal Computer World 2009 February
/
PCWFEB09.iso
/
Software
/
Resources
/
Chat & Communication
/
Digsby build 37
/
digsby_setup.exe
/
lib
/
common
/
logger.pyo
(
.txt
)
< prev
next >
Wrap
Python Compiled Bytecode
|
2008-10-13
|
13KB
|
314 lines
# Source Generated with Decompyle++
# File: in.pyo (Python 2.5)
from __future__ import with_statement
try:
_
except:
import gettext
gettext.install('Digsby')
import sys
import time
import re
from common import profile
from datetime import datetime, timedelta
from util import soupify, Storage as S, tail, boolify, fromutc
from path import path
from digsby import iswidget
from traceback import print_exc
from logging import getLogger
log = getLogger('logger')
from common.message import Message
import libxml2
def get_default_logging_dir():
lp = localprefs
import prefs
localprefs = lp()
return path(localprefs['chatlogdir']) / DEFAULT_LOG_DIR_NAME / profile.username
DEFAULT_LOG_DIR_NAME = u'Digsby Logs'
LOGSIZE_PARSE_LIMIT = 15360
def buddy_path(account, buddy):
return path(account.name).joinpath(account.username, buddy.name + '_' + buddy.service)
message_timestamp_fmt = '%Y-%m-%d %H:%M:%S'
message_timestamp_fmt_OLD = '%Y-%m-%d %H:%M'
filename_format_re = re.compile('\\d{4}-\\d{2}-\\d{2}\\..*')
message_shorttime_fmt = '%H:%M:%S %p'
html_header = u'<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\n "http://www.w3.org/TR/html4/strict.dtd">\n<HTML>\n <HEAD>\n <TITLE>%(title)s</TITLE>\n <style>\n .buddy { font-weight: bold; }\n .buddy:after { content: ":" }\n\n .time {\n color: #a0a0a0;\n font-family: monaco, courier new, monospace;\n font-size: 75%%;\n }\n .time:hover { color: black; }\n\n .outgoing { background-color: #efefef; }\n .incoming { background-color: #ffffff; }\n </style>\n <script type="text/javascript">\n//<![CDATA[\n function convert_time(datetime){\n var dt = datetime.split(" ");\n var date = dt[0].split("-");\n var time = dt[1].split(":");\n var t = new Date;\n t.setUTCFullYear(date[0],date[1]-1,date[2]);\n t.setUTCHours(time[0],time[1],time[2]);\n return t.toLocaleTimeString();\n }\n\n function utc_to_local(){\n var node;\n for (var i=0; i<document.body.childNodes.length; i++){\n node = document.body.childNodes[i];\n if(node.nodeType == 1 && node.className.match("message")){\n var showtime = convert_time(node.getAttribute(\'timestamp\'));\n var newspan = \'<span class="time">(\' + showtime + \') </span>\';\n var msgnode = node;\n msgnode.innerHTML = newspan + msgnode.innerHTML;\n }\n }\n }\n//]]>\n </script>\n </HEAD>\n <BODY onload="utc_to_local()">\n'
html_log_entry = '<div class="%(type)s message" auto="%(auto)s" timestamp="%(timestamp)s"><span class="buddy">%(buddy)s</span> <span class="msgcontent">%(message)s</span></div>\n'
class Logger(object):
def __init__(self, output_dir = None, log_ims = True, log_chats = True, log_widgets = False):
self.OutputDir = output_dir
self.OutputType = 'html'
self.LogChats = log_chats
self.LogIMs = log_ims
self.LogWidgets = log_widgets
def on_message(self, messageobj = None, convo = None):
if not self.should_log_message(messageobj):
return None
output = self.generate_output(messageobj)
written_size = self.write_output(output, messageobj)
try:
buddy = messageobj.conversation.buddy
except AttributeError:
pass
buddy.increase_log_size(written_size)
def should_log_message(self, messageobj):
if messageobj is None:
return False
convo = messageobj.conversation
if convo.ischat and not (self.LogChats):
return False
elif not (convo.ischat) and not (self.LogIMs):
return False
elif not (self.LogWidgets) and iswidget(convo.buddy):
return False
elif messageobj.buddy is None:
return False
elif not messageobj.buddy.protocol.should_log(messageobj):
return False
return True
def history_for(self, account, buddy):
log.info('history_for(%r, %r)', account, buddy)
files = self.logfiles_for(account, buddy)
log.info('%d %s log files found', len(files), self.OutputType)
if not files:
return iter([])
files.sort(reverse = True)
return history_from_files(files, 'html')
def logsize(self, account, buddy):
return sum((lambda .0: for f in .0:
f.size)(self.logfiles_for(account, buddy)))
def logfiles_for(self, account, buddy):
logdir = self.pathfor(account, buddy)
if not logdir.exists():
return []
return list((lambda .0: for f in .0:
if filename_format_re.match(f.name):
fcontinue)(logdir.files('*.' + self.OutputType)))
def pathfor(self, account, buddy):
return self.OutputDir.joinpath(buddy_path(account, buddy))
def set_outputdir(self, val):
self.output_dir = None if val is not None else get_default_logging_dir()
def get_outputdir(self):
return self.output_dir
OutputDir = property(get_outputdir, set_outputdir, doc = 'where to write logs')
def write_output(self, output, messageobj):
convo = messageobj.conversation
proto = convo.protocol
datefilename = fromutc(messageobj.timestamp).date().isoformat()
pathelems = (buddy_path(proto, convo.buddy), datefilename)
p = path(path(self.OutputDir).joinpath(*pathelems) + '.' + self.OutputType)
if not p.parent.exists():
p.parent.makedirs()
written_size = 0
if not p.exists():
header = globals()['generate_header_' + self.OutputType](messageobj, self.output_encoding)
written_size += len(header)
p.write_bytes(header)
written_size += len(output)
p.write_bytes(output, append = p.exists())
return written_size
def generate_output(self, messageobj):
return globals()['generate_output_' + self.OutputType](messageobj, self.output_encoding)
output_encoding = 'utf-8'
def generate_header_html(messageobj, encoding):
c = messageobj.conversation
datefmt = messageobj.timestamp.date().isoformat()
if c.ischat:
title = 'Chat in %s on %s' % (c.name, datefmt)
else:
title = 'IM Logs with %s on %s' % (c.buddy.name, datefmt)
return (html_header % dict(title = title.encode('xml'))).encode(encoding, 'replace')
def generate_output_html(m, encoding = 'utf-8'):
return (None % html_log_entry(dict = 'buddy' if m.buddy is not None else '', timestamp = m.timestamp.strftime(message_timestamp_fmt), message = m.message, type = m.type, auto = getattr(m, 'auto', False))).encode(encoding, 'replace')
class_buddy = {
'class': 'buddy' }
class_message = {
'class': 'message' }
class_msgcontent = {
'class': 'msgcontent' }
def message_divs(tag):
if tag.name == 'div':
pass
return 'message' in dict(tag.attrs).get('class', '')
def parse_html_fast(html):
doc = libxml2.htmlParseDoc(html, 'utf-8')
try:
divs = doc.xpathEval('//html/body/div')
messages = []
for div in divs:
message_type = div.properties.content
if 'message' not in message_type:
continue
type = message_type.replace('message', '').strip()
if type not in ('incoming', 'outgoing'):
log.critical('got an unknown message type: %s', type)
raise ValueError('unknown message type')
props = div.properties
(auto, timestamp, buddyname, message) = (None, None, None, None)
while props:
name = props.name
value = props.content
if name == 'auto':
auto = boolify(value)
elif name == 'timestamp':
timestamp = parse_timestamp(value)
props = props.get_next()
child = div.children
while child:
props = child.properties
if props is not None:
attrs = props.content
if attrs == 'buddy':
buddyname = child.content.decode('utf-8')
elif attrs == 'msgcontent':
message = child.serialize().decode('utf-8')
child = child.next
if all((lambda .0: for a in .0:
a is not None)((auto, timestamp, buddyname, message))):
messages.append(Message(buddy = S(name = buddyname), timestamp = timestamp, message = message, type = type, auto = auto))
continue
raise ValueError('not all attributes could be parsed: %r' % ((auto, timestamp, buddyname, message),))
log.info('parse_html_fast with %d bytes returning %d messages', len(html), len(messages))
return messages
finally:
doc.freeDoc()
def parse_html_slow(html):
soup = soupify(html, markupMassage = ((br_re, (lambda m: '<br />')),))
messages = []
strptime = datetime.strptime
for div in soup.findAll(message_divs):
try:
buddyname = div.findAll('span', class_buddy)[0].renderContents(None)
timestamp = parse_timestamp(div['timestamp'])
message = div.findAll('span', class_msgcontent)[0].renderContents(None)
type = div['class'].replace('message', '').strip()
auto = boolify(div.get('auto', 'false'))
except Exception:
print_exc()
continue
messages.append(Message(buddy = S(name = buddyname), timestamp = timestamp, message = message, type = type, auto = auto))
log.info('parse_html_slow with %d bytes returning %d messages', len(html), len(messages))
return messages
USE_LIBXML2_HTML_PARSER = False
def parse_html(html, last_parsed = [
None,
None]):
digest = hash(html)
(msghash, oldmessages) = last_parsed
if msghash == digest:
return oldmessages
if not USE_LIBXML2_HTML_PARSER or sys.platform == 'darwin':
messages = parse_html_slow(html)
else:
try:
messages = parse_html_fast(html)
except Exception:
messages = parse_html_slow(html)
log.info('parsed slow: got %d messages', len(messages))
last_parsed[0] = digest
last_parsed[1] = messages
return messages
def parse_timestamp(timestamp):
try:
return datetime.strptime(timestamp, message_timestamp_fmt)
except Exception:
return datetime.strptime(timestamp, message_timestamp_fmt_OLD)
def chat_path(account, room_name, *additional):
return path(account.name).joinpath(account.username, *additional)
def history_from_files(files, logtype = 'html', encoding = 'utf-8'):
parse = globals()['parse_' + logtype]
for logfile in files:
try:
bytes = tail(logfile, LOGSIZE_PARSE_LIMIT, encoding = encoding)
except Exception:
print_exc()
continue
for msg in reversed(parse(bytes)):
yield msg
if len(bytes) < logfile.size:
break
continue
import re
real_br = '<br />'
br_re = re.compile('<br\\s*/?>', re.IGNORECASE)
brfix = lambda s: br_re.sub(real_br, s)